package com.xmcowell.sportpacemaster.utils.mqtt;

import android.annotation.TargetApi;
import android.app.AlarmManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Message;
import android.util.Log;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.ibm.mqtt.IMqttClient;
import com.ibm.mqtt.MqttClient;
import com.ibm.mqtt.MqttException;
import com.ibm.mqtt.MqttPersistence;
import com.ibm.mqtt.MqttPersistenceException;
import com.ibm.mqtt.MqttSimpleCallback;
import com.xmcowell.sportpacemaster.R;
import com.xmcowell.sportpacemaster.config.Contents;
import com.xmcowell.sportpacemaster.config.UrlConfig;
import com.xmcowell.sportpacemaster.layout.login.Login2Activity;
import com.xmcowell.sportpacemaster.utils.PreferencesUtils;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;

/* 
 * PushService that does all of the work.
 * Most of the logic is borrowed from KeepAliveService.
 * http://code.google.com/p/android-random/source/browse/trunk/TestKeepAlive/src/org/devtcg/demo/keepalive/KeepAliveService.java?r=219
 */
public class PushService extends Service {
    // this is the log tag
    public static final String TAG = "DemoPushService";

    // the IP address, where your MQTT broker is running.
    private static final String MQTT_HOST = UrlConfig.MQTT_HOST; // 配置服务器IP
    // the port at which the broker is running.
    private static int MQTT_BROKER_PORT_NUM = 18838; // 配置服务器端口
    // Let's not use the MQTT persistence.
    private static MqttPersistence MQTT_PERSISTENCE = null;
    // We don't need to remember any state between the connections, so we use a
    // clean start.
    private static boolean MQTT_CLEAN_START = true;
    // Let's set the internal keep alive for MQTT to 15 mins. I haven't tested
    // this value much. It could probably be increased.
    private static short MQTT_KEEP_ALIVE = 60 * 15;
    // Set quality of services to 0 (at most once delivery), since we don't want
    // push notifications
    // arrive more than once. However, this means that some messages might get
    // lost (delivery is not guaranteed)
    private static int[] MQTT_QUALITIES_OF_SERVICE = {2};
    private static int MQTT_QUALITY_OF_SERVICE = 2;
    // The broker should not retain any messages.
    private static boolean MQTT_RETAINED_PUBLISH = false;

    // MQTT client ID, which is given the broker. In this example, I also use
    // this for the topic header.
    // You can use this to run push notifications for multiple apps with one
    // MQTT broker.
    public static String MQTT_CLIENT_ID = "";

    // These are the actions for the service (name are descriptive enough)
    private static final String ACTION_START = MQTT_CLIENT_ID + ".START";
    private static final String ACTION_STOP = MQTT_CLIENT_ID + ".STOP";
    private static final String ACTION_KEEPALIVE = MQTT_CLIENT_ID
            + ".KEEP_ALIVE";
    private static final String ACTION_RECONNECT = MQTT_CLIENT_ID
            + ".RECONNECT";
    private static String msg = "";

    // Connection log for the push service. Good for debugging.
//    private ConnectionLog mLog;

    // Connectivity manager to determining, when the phone loses connection
    private ConnectivityManager mConnMan;
    // Notification manager to displaying arrived push notifications
    private NotificationManager mNotifMan;

    // Whether or not the service has been started.
    private boolean mStarted;

    // This the application level keep-alive interval, that is used by the
    // AlarmManager
    // to keep the connection active, even when the device goes to sleep.
    private static final long KEEP_ALIVE_INTERVAL = 1000 * 60 * 28;

    // Retry intervals, when the connection is lost.
    private static final long INITIAL_RETRY_INTERVAL = 1000 * 10;
    private static final long MAXIMUM_RETRY_INTERVAL = 1000 * 60 * 30;

    // Preferences instance
    private SharedPreferences mPrefs;
    // We store in the preferences, whether or not the service has been started
    public static final String PREF_STARTED = "isStarted";
    // We also store the deviceID (target)
    public static final String PREF_DEVICE_ID = "deviceID";// 私有标识
    public static final String PUBLIC_ID = "publicID";// 公共标识
    // We store the last retry interval
    public static final String PREF_RETRY = "retryInterval";

    // Notification title
    public static String NOTIF_TITLE = "昵称"; // 标题,建议放昵称
    // Notification id
    // private static final int NOTIF_CONNECTED = 0;

    // This is the instance of an MQTT connection.
    private MQTTConnection mConnection, mConnection_all;
    private static long mStartTime;

    private static int id = 0;// 如果要做成一个人一条用UID

    // Static method to start the service
    public static void actionStart(Context ctx) {
        Intent i = new Intent(ctx, PushService.class);
        i.setAction(ACTION_START);
        ctx.startService(i);
    }

    // Static method to stop the service
    public static void actionStop(Context ctx) {
        Intent i = new Intent(ctx, PushService.class);
        i.setAction(ACTION_STOP);
        ctx.startService(i);
    }

    // Static method to send a keep alive message
    public static void actionPing(Context ctx) {
        Intent i = new Intent(ctx, PushService.class);
        i.setAction(ACTION_KEEPALIVE);
        ctx.startService(i);
    }

    @Override
    public void onCreate() {
        super.onCreate();

        log("Creating service");
        mStartTime = System.currentTimeMillis();
//        try {
//            mLog = new ConnectionLog();
//            Log.i(TAG, "Opened log at " + mLog.getPath());
//        } catch (IOException e) {
//            Log.e(TAG, "Failed to open log", e);
//        }

        // Get instances of preferences, connectivity manager and notification
        // manager
        mPrefs = getSharedPreferences(TAG, MODE_PRIVATE);
        mConnMan = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
        mNotifMan = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

		/*
         * If our process was reaped by the system for any reason we need to
		 * restore our state with merely a call to onCreate. We record the last
		 * "started" value and restore it here if necessary.
		 */
        handleCrashedService();
    }

    // This method does any necessary clean-up need in case the server has been
    // destroyed by the system
    // and then restarted
    private void handleCrashedService() {
        if (wasStarted() == true) {
            log("Handling crashed service...");
            // stop the keep alives
            stopKeepAlives();

            // Do a clean start
            start();
        }
    }

    @Override
    public void onDestroy() {
        unregisterReceiver(mConnectivityChanged);
    }

    @Override
    public void onStart(Intent intent, int startId) {
        super.onStart(intent, startId);
        log("Service started with intent=" + intent);

        if (intent == null || intent.getAction() == null) {
            stop();
            stopSelf();
            return;
        }

        // Do an appropriate action based on the intent.
        if (intent.getAction().equals(ACTION_STOP) == true) {
            stop();
            stopSelf();
        } else if (intent.getAction().equals(ACTION_START) == true) {
            start();
        } else if (intent.getAction().equals(ACTION_KEEPALIVE) == true) {
            keepAlive();
        } else if (intent.getAction().equals(ACTION_RECONNECT) == true) {
            if (isNetworkAvailable()) {
                reconnectIfNecessary();
            }
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    // log helper function
    private void log(String message) {
        log(message, null);
    }

    private void log(String message, Throwable e) {
        if (e != null) {
            Log.e(TAG, message, e);

        } else {
            Log.i(TAG, message);
        }

//        if (mLog != null) {
//            try {
//                mLog.println(message);
//            } catch (IOException ex) {
//            }
//        }
    }

    // Reads whether or not the service has been started from the preferences
    private boolean wasStarted() {
        return mPrefs.getBoolean(PREF_STARTED, false);
    }

    // Sets whether or not the services has been started in the preferences.
    private void setStarted(boolean started) {
        mPrefs.edit().putBoolean(PREF_STARTED, started).commit();
        mStarted = started;
    }

    private synchronized void start() {
        log("Starting service...");

        // Do nothing, if the service is already running.
        if (mStarted == true) {
            Log.w(TAG, "Attempt to start connection that is already active");
            return;
        }

        // Establish an MQTT connection
        connect();

        // Register a connectivity listener
        registerReceiver(mConnectivityChanged, new IntentFilter(
                ConnectivityManager.CONNECTIVITY_ACTION));
    }

    private synchronized void stop() {
        // Do nothing, if the service is not running.
        if (mStarted == false) {
            Log.w(TAG, "Attempt to stop connection not active.");
            return;
        }

        // Save stopped state in the preferences
        setStarted(false);

        // Remove the connectivity receiver
        // unregisterReceiver(mConnectivityChanged);
        // Any existing reconnect timers should be removed, since we explicitly
        // stopping the service.
        cancelReconnect();

        // Destroy the MQTT connection if there is one
        if (mConnection != null) {
            mConnection.disconnect();
            mConnection = null;
        }

    }


    //
    private synchronized void connect() {
        new Thread(new Runnable() {

            @Override
            public void run() {
                // TODO 自动生成的方法存根
                log("Connecting...");
                // fetch the device ID from the preferences.
                String deviceID = PreferencesUtils.getValueFromSPMap(getApplication(), PreferencesUtils.Keys.TOPIC);
                // Create a new connection only if the device id is not NULL
                if (deviceID == null) {
                    log("Device ID not found.");
                } else {
                    try {
                        mConnection = new MQTTConnection(MQTT_HOST, deviceID);

                    } catch (MqttException e) {
                        // Schedule a reconnect, if we failed to connect
                        log("MqttException: "
                                + (e.getMessage() != null ? e.getMessage()
                                : "NULL"));
                        if (isNetworkAvailable()) {
                            scheduleReconnect(mStartTime);
                        }
                    }
                    setStarted(true);
                }
            }
        }).start();

    }

    private synchronized void keepAlive() {
        try {
            // Send a keep alive, if there is a connection.
            if (mStarted == true && mConnection != null) {
                mConnection.sendKeepAlive();
            }
        } catch (MqttException e) {
            log("MqttException: "
                    + (e.toString() != null ? e.toString() : "NULL"), e);

            mConnection.disconnect();
            mConnection = null;
            cancelReconnect();
        }
    }

    // Schedule application level keep-alives using the AlarmManager
    private void startKeepAlives() {
        Intent i = new Intent();
        i.setClass(this, PushService.class);
        i.setAction(ACTION_KEEPALIVE);
        PendingIntent pi = PendingIntent.getService(this, 0, i, 0);
        AlarmManager alarmMgr = (AlarmManager) getSystemService(ALARM_SERVICE);
        alarmMgr.setRepeating(AlarmManager.RTC_WAKEUP,
                System.currentTimeMillis() + KEEP_ALIVE_INTERVAL,
                KEEP_ALIVE_INTERVAL, pi);
    }

    // Remove all scheduled keep alives
    private void stopKeepAlives() {
        Intent i = new Intent();
        i.setClass(this, PushService.class);
        i.setAction(ACTION_KEEPALIVE);
        PendingIntent pi = PendingIntent.getService(this, 0, i, 0);
        AlarmManager alarmMgr = (AlarmManager) getSystemService(ALARM_SERVICE);
        alarmMgr.cancel(pi);
    }

    // We schedule a reconnect based on the starttime of the service
    public void scheduleReconnect(long startTime) {
        // the last keep-alive interval
        long interval = mPrefs.getLong(PREF_RETRY, INITIAL_RETRY_INTERVAL);

        // Calculate the elapsed time since the start
        long now = System.currentTimeMillis();
        long elapsed = now - startTime;

        // Set an appropriate interval based on the elapsed time since start
        if (elapsed < interval) {
            interval = Math.min(interval * 4, MAXIMUM_RETRY_INTERVAL);
        } else {
            interval = INITIAL_RETRY_INTERVAL;
        }

        log("Rescheduling connection in " + interval + "ms.");

        // Save the new internval
        mPrefs.edit().putLong(PREF_RETRY, interval).commit();

        // Schedule a reconnect using the alarm manager.
        Intent i = new Intent();
        i.setClass(this, PushService.class);
        i.setAction(ACTION_RECONNECT);
        PendingIntent pi = PendingIntent.getService(this, 0, i, 0);
        AlarmManager alarmMgr = (AlarmManager) getSystemService(ALARM_SERVICE);
        alarmMgr.set(AlarmManager.RTC_WAKEUP, now + interval, pi);
    }

    // Remove the scheduled reconnect
    public void cancelReconnect() {
        Intent i = new Intent();
        i.setClass(this, PushService.class);
        i.setAction(ACTION_RECONNECT);
        PendingIntent pi = PendingIntent.getService(this, 0, i, 0);
        AlarmManager alarmMgr = (AlarmManager) getSystemService(ALARM_SERVICE);
        alarmMgr.cancel(pi);
    }

    private synchronized void reconnectIfNecessary() {
        if (mStarted == true && mConnection == null) {
            log("Reconnecting...");
            connect();
        }
    }

    // This receiver listeners for network changes and updates the MQTT
    // connection
    // accordingly
    private BroadcastReceiver mConnectivityChanged = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            // Get network info
            NetworkInfo info = (NetworkInfo) intent
                    .getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);

            // Is there connectivity?
            boolean hasConnectivity = (info != null && info.isConnected()) ? true
                    : false;

            log("Connectivity changed: connected=" + hasConnectivity);

            if (hasConnectivity) {
                reconnectIfNecessary();
            } else if (mConnection != null) {
                // if there no connectivity, make sure MQTT connection is
                // destroyed
                mConnection.disconnect();
                cancelReconnect();
                mConnection = null;
            }
        }
    };

    // Display the topbar notification
    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    private void showNotification(String text, Intent zCallIntent) {
//		Notification n = new Notification();
//		n.flags |= Notification.FLAG_SHOW_LIGHTS;
//		n.flags |= Notification.FLAG_AUTO_CANCEL;
//
//		n.defaults = Notification.DEFAULT_ALL;
//
//		n.icon = R.mipmap.logo_city;// 通知显示的图标
//
//		n.when = System.currentTimeMillis();
//		// Simply open the parent activity
//		zCallIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        PendingIntent pi = PendingIntent.getActivity(this, id, zCallIntent, 0);// 点击通知栏后进入的
//
//		// Change the name of the notification here
//		n.setLatestEventInfo(this, NOTIF_TITLE, text, pi);

        Notification n = null;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            n = new Notification.Builder(this)
                    .setAutoCancel(true)
                    .setContentTitle(NOTIF_TITLE)
                    .setContentText(text)
                    .setContentIntent(pi)
                    .setSmallIcon(R.drawable.bg_no_oil)
                    .setWhen(System.currentTimeMillis())
                    .setDefaults(Notification.DEFAULT_ALL)
                    .build();
        }
        mNotifMan.notify(id, n);
        // id++;
    }

    // Check if we are online
    private boolean isNetworkAvailable() {
        NetworkInfo info = mConnMan.getActiveNetworkInfo();
        if (info == null) {
            return false;
        }
        return info.isConnected();
    }

    // This inner class is a wrapper on top of MQTT client.
    private class MQTTConnection implements MqttSimpleCallback {
        IMqttClient mqttClient = null;

        // Creates a new connection given the broker address and initial topic
        public MQTTConnection(String brokerHostName, String initTopic)
                throws MqttException {
            // Create connection spec
            String mqttConnSpec = "tcp://" + brokerHostName + "@"
                    + MQTT_BROKER_PORT_NUM;
            // Create the client and connect 下面可能会内存泄露 ooM
            mqttClient = MqttClient.createMqttClient(mqttConnSpec,
                    MQTT_PERSISTENCE);
//			if(TemDate.getInstance().getUserInfo() == null)
//				return;
//			String id =TemDate.getInstance().getUserInfo().getId().replace("-","");
            String id = Contents.IMEI;
            String clientID = MQTT_CLIENT_ID + "/"
                    + id;
            mqttClient.connect(clientID, MQTT_CLEAN_START, MQTT_KEEP_ALIVE);

            // register this client app has being able to receive messages
            mqttClient.registerSimpleHandler(this);

            // Subscribe to an initial topic, which is combination of client ID
            // and device ID.
            // initTopic = initTopic;
            subscribeToTopic(initTopic);

            log("Connection established to " + brokerHostName + " on topic "
                    + initTopic);

            // Save start time
            mStartTime = System.currentTimeMillis();
            // Star the keep-alives
            startKeepAlives();
        }

        // Disconnect
        public void disconnect() {
            try {
                stopKeepAlives();
                mqttClient.disconnect();
            } catch (MqttPersistenceException e) {
                log("MqttException"
                                + (e.toString() != null ? e.toString() : " NULL"),
                        e);
            }
        }

        /*
         * Send a request to the message broker to be sent messages published
         * with the specified topic name. Wildcards are allowed.
         */
        private void subscribeToTopic(String topicName) throws MqttException {

            if ((mqttClient == null) || (mqttClient.isConnected() == false)) {
                // quick sanity check - don't try and subscribe if we don't have
                // a connection
                log("Connection error" + "No connection");
            } else {
                ArrayList<String> result = new Gson()
                        .fromJson(
                                topicName,
                                new TypeToken<ArrayList<String>>() {
                                }.getType());
                String[] topics = new String[1];
                for (int i = 0; i < result.size(); i++) {
                    topics[0] = result.get(i);
                    mqttClient.subscribe(topics, MQTT_QUALITIES_OF_SERVICE);
                }
//				String[] public_topics = { publicID };
//				mqttClient.subscribe(public_topics, MQTT_QUALITIES_OF_SERVICE);
            }
        }

        /*
         * Sends a message to the message broker, requesting that it be
         * published to the specified topic.
         */
        private void publishToTopic(String topicName, String message)
                throws MqttException {
            if ((mqttClient == null) || (mqttClient.isConnected() == false)) {
                // quick sanity check - don't try and publish if we don't have
                // a connection
                log("No connection to public to");
            } else {
                mqttClient.publish(topicName, message.getBytes(),
                        MQTT_QUALITY_OF_SERVICE, MQTT_RETAINED_PUBLISH);
            }
        }

        /*
         * Called if the application loses it's connection to the message
         * broker.
         */
        public void connectionLost() throws Exception {
            log("Loss of connection" + "connection downed");
            stopKeepAlives();
            // null itself
            mConnection = null;
            if (isNetworkAvailable() == true) {
                Thread.sleep(10000);
                reconnectIfNecessary();
            }
        }

        /*
         * Called when we receive a message from the message broker.
         */
        public void publishArrived(String topicName, byte[] payload, int qos,
                                   boolean retained) {
            // Show a notification

            try {
                msg = new String(payload, "utf-8");// 中文编码
            } catch (UnsupportedEncodingException e) {
                // TODO 自动生成的 catch 块
                e.printStackTrace();
            }
            log("Got message: " + msg);
            sendMsg(msg);

//			if (TemDate.getInstance().getUserInfo() != null){
//				String type = topicName.replace(TemDate.getInstance().getUserInfo().getId(),"");
//				sendMsg(msg,type);
//			}

        }

        public void sendKeepAlive() throws MqttException {
            log("Sending keep alive");
            // publish to a keep-alive topic
            publishToTopic(MQTT_CLIENT_ID + "/keepalive",
                    mPrefs.getString(PREF_DEVICE_ID, ""));
        }
    }

    private boolean is_push;
    private boolean is_notice;
    private String keycode="";//保存推送过来的keycode，防止收到重复的推送
    /**
     * 对获得字符串json解析
     */
    public void sendMsg(String message) {
//        二维码：4b4381ddb5f077e5_172.20.10.2_1502014749789
//        Got message: {"customdata":{"buliddatetime":"2017-08-06 18:19:14","loginvkeycode":"c6ad6ffd-3e13-4f19-9582-0f5ca97c9149","scanclientid":"17c30030-cc5f-4e09-9cad-780147b367de"},"alertmessage":"扫描二维码登陆"}
        try {
            JSONObject json = new JSONObject(message);
            String alertmessage = json.getString("alertmessage");
            if (alertmessage.equals("扫描二维码登陆")) {
                JSONObject customdata = json.getJSONObject("customdata");
                String buliddatetime = customdata.getString("buliddatetime");
                String loginvkeycode = customdata.getString("loginvkeycode");
                String scanclientid = customdata.getString("scanclientid");

                if (loginvkeycode.equals(keycode)){

                }else{
                    keycode = loginvkeycode;
                    if (Login2Activity.instance != null && !Login2Activity.instance.isFinishing()) {
                        Bundle bundle = new Bundle();
                        bundle.putString("code", loginvkeycode);
                        bundle.putString("id", scanclientid);
                        Message msg = new Message();
                        msg.what = 0;
                        msg.setData(bundle);
                        Login2Activity.instance.mHandler.sendMessage(msg);
                    }
                }

            }
        } catch (JSONException e) {
            e.printStackTrace();
            Log.e(TAG,"消息推送出错，error = "+e.toString());
        }

    }

}